package tp.rest;

import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.util.JAXBSource;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.http.HTTPBinding;

import tp.model.City;
import tp.model.CityManager;
import tp.model.NoCityFound;
import tp.model.Position;

/**
 * Client de l'application.
 * @author Anton KISSELEFF & Nicolas LETHUILLIER
 */
public class MyClient {
	
	// ATTRIBUTS

	private static final QName qname = new QName("", "");
	private static final String url = "http://127.0.0.1:8080";
	
	private Service service;
	private JAXBContext jc;
	
	// CONSTRUCTEURS

	public MyClient() {
		try {
			jc = JAXBContext.newInstance(CityManager.class, City.class, Position.class,NoCityFound.class);
		} catch (JAXBException je) {
			System.out.println("Cannot create JAXBContext " + je);
		}
	}
	
	// COMMANDES
	
	/**
	 * Demande au serveur de supprimer la ville passée en argument.
	 * @param city la {@link City} à supprimer
	 * @throws JAXBException en cas d'échec de transmission
	 */
	public void deleteCity(City city) throws JAXBException {
		send("DELETE", city, city.toString().replace(" ", "_"));
	}
	
	/**
	 * Demande au serveur de supprimer toutes les villes.
	 * @throws JAXBException en cas d'échec de transmission
	 */
	public void deleteAllCities() throws JAXBException {
		send("DELETE", new City(), "all");
	}
	
	/**
	 * Demande au serveur une ville à partir de sa position exacte.
	 * @param position La {@link Position} de la {@link City} à trouver.
	 * @throws JAXBException en cas d'échec de transmission
	 */
	public void searchForCity(Position position) throws JAXBException {
		searchForCity(position, false);
	}

	/**
	 * Dermande au serveur une ville à partir de sa position.
	 * Si near == true, demande au serveur de chercher toutes les villes proches de cette position
	 *  (selon une distance définie dans {@link CityManager}).
	 * @param position la {@link Position} de la {@link City} à trouver.
	 * @param near indique s'il faut chercher les villes proches de cette position
	 * @throws JAXBException en cas d'échec de transmission
	 */
	public void searchForCity(Position position, boolean near) throws JAXBException {
		if (near) {
			send("POST", position, "near");
		} else {
			send("POST", position);
		}
	}
	
	/**
	 * Demande au serveur la liste de toutes les {@link City}.
	 * @throws JAXBException en cas d'échec de transmission
	 */
	public void getAllCities() throws JAXBException {
		send("GET", new City(), "all");
	}
	
	/**
	 * Demande au serveur l'ensemble des {@link City} dont le nom contient cityName.
	 * Pour chercher selon plusieurs mots-clés, on utilisera le symbole &.
	 *  exemple : "et&te" cherchera toutes les villes dont le nom contient "et" ou "te" 
	 * La recherche est insensible à la casse.
	 * @param cityName Nom de la {@link City} à rechercher
	 * @throws JAXBException en cas d'échec de transmission
	 */
	public void getCity(String cityName) throws JAXBException {
		send("GET", new City(), cityName);
	}
	
	/**
	 * Demande au serveur d'ajouter la {@link City} passée en argument.
	 * @param city {@link City} à ajouter
	 * @throws JAXBException en cas d'échec de transmission
	 */
	public void addCity(City city) throws JAXBException {
		send("PUT", city);
	}

	/**
	 * Affiche le contenu de la source s.
	 * @param s la source à transformer pour l'affichage
	 */
	public void printSource(Source s) {
		try {
			System.out.println("============================= Response Received =========================================");
			TransformerFactory factory = TransformerFactory.newInstance();
			Transformer transformer = factory.newTransformer();
			try {
				transformer.transform(s, new StreamResult(System.out));
			} catch (TransformerException ex) {
				System.out.println("Erreur à la transformation !");
			}
			System.out.println("\n======================================================================");
		} catch (Exception e) {
			System.out.println(e);
		}
	}
	
	public void scenar1() throws JAXBException {
		System.out.println("Afficher toutes les villes");
		getAllCities();
		
		System.out.println("Supprimer toutes les villes");
		deleteAllCities();
		
		System.out.println("Afficher toutes les villes");
		getAllCities();
		
		City rouen = new City("Rouen",49.443889,1.103333,"France");
		System.out.println("Ajouter " + rouen);
		addCity(rouen);
		
		City moga = new City("Mogadiscio",2.333333,48.85,"Somalie");
		System.out.println("Ajouter " + moga);
		addCity(moga);
		
		System.out.println("Ajouter " + rouen);
		addCity(rouen);
		
		City bihorel = new City("Bihorel",49.455278,1.116944,"France");
		System.out.println("Ajouter " + bihorel);
		addCity(bihorel);
		
		City londres = new City("Londres",51.504872,-0.07857,"Angleterre");
		System.out.println("Ajouter " + londres);
		addCity(londres);
		
		City paris =  new City("Paris",48.856578,2.351828,"France");
		System.out.println("Ajouter " + paris);
		addCity(paris);
		
		City parisCanada =  new City("Paris",43.2,-80.38333,"Canada");
		System.out.println("Ajouter " + parisCanada);
		addCity(parisCanada);
		
		System.out.println("Afficher toutes les villes");
		getAllCities();
		
		City villersBocageA =  new City("Villers Bocage",49.083333,-0.65,"France");
		System.out.println("Ajouter " + villersBocageA);
		addCity(villersBocageA);
		
		City villersBocageB =  new City("Villers Bocage",50.021858,3.326126,"France");
		System.out.println("Ajouter " + villersBocageB);
		addCity(villersBocageB);
		
		System.out.println("Afficher toutes les villes");
		getAllCities();
		
		System.out.println("Supprimer " + villersBocageA);
		deleteCity(villersBocageA);
		
		System.out.println("Afficher toutes les villes");
		getAllCities();
		
		System.out.println("Supprimer " + londres);
		deleteCity(londres);
		
		System.out.println("Supprimer " + londres);
		deleteCity(londres);
		
		Position p = new Position(49.443889, 1.103333);
		System.out.println("Chercher la ville à la position " + p);
		searchForCity(p);
		
		p = new Position(49.083333,-0.65);
		System.out.println("Chercher la ville à la position " + p);
		searchForCity(p);
		
		p = new Position(43.2,-80.38);
		System.out.println("Chercher la ville à la position " + p);
		searchForCity(p);
		
		p = new Position(48.85, 2.34);
		System.out.println("Chercher les villes proches de " + p);
		searchForCity(p, true);
		
		p = new Position(45,64);
		System.out.println("Chercher les villes proches de " + p);
		searchForCity(p, true);
		
		p = new Position(49.45, 1.11);
		System.out.println("Chercher les villes proches de " + p);
		searchForCity(p, true);
		
		String name = "Mogadiscio";
		System.out.println("Chercher " + name);
		getCity(name);
		
		name = "Paris";
		System.out.println("Chercher " + name);
		getCity(name);
		
		name = "Hyrule";
		System.out.println("Chercher " + name);
		getCity(name);
		
		System.out.println("Supprimer toutes les villes");
		deleteAllCities();
		
		System.out.println("Afficher toutes les villes");
		getAllCities();
	}
	
	// OUTILS
	
	private void send(String method, Object arg) throws JAXBException {
		send(method, arg, null);
	}
	
	private void send(String method, Object arg, String urlArg) throws JAXBException {
		service = Service.create(qname);
		service.addPort(qname, HTTPBinding.HTTP_BINDING, url + (urlArg == null ? "" : "/?" + urlArg));
		Dispatch<Source> dispatcher = service.createDispatch(qname,
				Source.class, Service.Mode.MESSAGE);
		Map<String, Object> requestContext = dispatcher.getRequestContext();
		requestContext.put(MessageContext.HTTP_REQUEST_METHOD, method);
		Source result = dispatcher.invoke(new JAXBSource(jc, arg));
		printSource(result);
	}
	
	// MAIN

	public static void main(String args[]) throws Exception {
		MyClient client = new MyClient();
		client.scenar1();
	}
}
